#include "StdAfx.h"
#include "TerrainApp.h"



const D3DXVECTOR3 TerrainApp::UP = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
const float TerrainApp::PIOVER2 = 0.5f * PI;
const float TerrainApp::PI2 = 2.0f * PI;

void TerrainApp::initApp()
{
	D3DApp::initApp();

	fx::InitAll(this->md3dDevice);
	InputLayout::InitAll(this->md3dDevice);
	GetTextureMgr().init(this->md3dDevice);

	initResources();
	GetCamera().position() = D3DXVECTOR3(0.0f, 10.0f, -10.0f);

	mSky.init(md3dDevice, 5000.0f);
	mWaves.init(md3dDevice, 257, 257, 0.5f, 0.03f, 3.25f);
	mTerrain.init(md3dDevice, 129, 129, 1.0f);

	buildLights();

	mInitCursorPos.x = this->mClientWidth / 2;
	mInitCursorPos.y = this->mClientHeight / 2;
	mPrevCursorPos = mInitCursorPos;
	ClientToScreen(this->mhMainWnd, &mPrevCursorPos);
	SetCursorPos(mPrevCursorPos.x, mPrevCursorPos.y);
}

void TerrainApp::onResize()
{
	D3DApp::onResize();

	float aspect = float(mClientWidth) / mClientHeight;
	//D3DXMatrixPerspectiveFovLH(&mProj, 0.25f*PI, aspect, 1.0f, 1000.0f);
	GetCamera().setLens(0.25f * PI, aspect, 1.0f, 1000.0f);
}

void TerrainApp::updateScene(float dt)
{
	D3DApp::updateScene(dt);

	updateDirection(dt);
	updatePosition(dt);
	GetCamera().rebuildView();
	interactAtSea();

	//D3DXVECTOR3 target = mViewPos + mViewForward;
	//D3DXMatrixLookAtLH(&mView, &mViewPos, &target, &UP);

	mWaves.update(dt);
}

void TerrainApp::drawScene()
{
	D3DApp::drawScene();
	float blendFactors[] = {0.0f, 0.0f, 0.0f, 0.0f};
	md3dDevice->OMSetDepthStencilState(0, 0);
	md3dDevice->OMSetBlendState(0, blendFactors, 0xffffffff);
	md3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	md3dDevice->IASetInputLayout(InputLayout::PosNormalTex);

	// set per frame constants
	//mfxEyePosVar->SetRawValue( &mViewPos, 0, sizeof(mViewPos) );
	mfxEyePosVar->SetRawValue( &GetCamera().position(), 0, sizeof(D3DXVECTOR3) );
	mfxLightVar->SetRawValue( &mParallelLight, 0, sizeof(mParallelLight) );

	D3D10_TECHNIQUE_DESC techDesc;
	mTech->GetDesc(&techDesc);
	D3DXMATRIX world;
	D3DXMATRIX WVP;
	D3DXMATRIX texMatrix;
	D3DXMATRIX view = GetCamera().view();
	D3DXMATRIX proj = GetCamera().proj();

	this->mfxCubeMapVar->SetResource(this->mEnvMapRV);

	md3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	md3dDevice->IASetInputLayout(InputLayout::PosNormalTex);
	for (UINT p = 0; p < techDesc.Passes; ++p) {
		ID3D10EffectPass *pass = mTech->GetPassByIndex(p);

		// land
		/*
		world = mTerrain.getWorldMatrix();
		WVP = world * view * proj;
		mfxWVPVar->SetMatrix( (float*)&WVP );
		mfxWorldVar->SetMatrix( (float*) &world );
		mfxDiffuseMapVar->SetResource(mTerrain.getDiffuseMap(0) );
		mfxSpecMapVar->SetResource(mTerrain.getSpecularMap(mDefaultSpecMapRV));
		mfxCubeMapEnabledVar->SetBool(false);
		mfxReflectMtrlVar->SetFloatVector((float*)&mReflectNone);
		pass->Apply(0);
		mTerrain.draw();
		*/

		// waves
		world = mWaves.getWorldMatrix();
		texMatrix = mWaves.getTexMatrix();
		WVP = world * view * proj;

		mfxWVPVar->SetMatrix( (float*)&WVP );
		mfxWorldVar->SetMatrix( (float*)&world );
		mfxTexMtxVar->SetMatrix( (float*)&texMatrix );
		mfxDiffuseMapVar->SetResource(mWaves.getDiffuseMap(0));
		mfxSpecMapVar->SetResource(mWaves.getSpecMap(mDefaultSpecMapRV));
		mfxCubeMapEnabledVar->SetBool(true);
		mfxReflectMtrlVar->SetFloatVector((float*)&mReflectSoso);
		pass->Apply(0);
		mWaves.draw();
	}

	mSky.draw();


	// log
	RECT rect = {5, 5, 0, 0};
	//mFont->DrawText(0, mFrameStats.c_str(), -1, &rect, DT_NOCLIP, BLACK);

	// show
	mSwapChain->Present(0, 0);

}
LRESULT TerrainApp::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		case WM_SETCURSOR:
			ShowCursor(false);
			return 0;
		case WM_LBUTTONDOWN:
			SetCapture(this->mhMainWnd);
			return 0;
		case WM_LBUTTONUP:
			mLeftMouseUp = true;
			ReleaseCapture();
			return 0;
		case WM_KEYDOWN:
			if ( wParam == VK_ESCAPE )
				DestroyWindow(this->mhMainWnd);
			return 0;
	}

	return D3DApp::msgProc(msg, wParam, lParam);
}

void TerrainApp::updateDirection(float dt) {
	float kRoll = 0.003f;
	POINT currCursorPos;

	GetCursorPos(&currCursorPos);
	mPrevCursorPos = mInitCursorPos;
	ClientToScreen(this->mhMainWnd, &mPrevCursorPos);
	float dx = float(currCursorPos.x - mPrevCursorPos.x);
	float dy = float(currCursorPos.y - mPrevCursorPos.y);
	
	SetCursorPos(mPrevCursorPos.x, mPrevCursorPos.y);
	GetCamera().rotateY(kRoll * dx);
	GetCamera().pitch(kRoll * dy);

	/*
	//mPrevCursorPos = currCursorPos;
	if (mViewRot.y < 0.1f-PIOVER2) mViewRot.y = 0.1f-PIOVER2;
	if (PIOVER2-0.1f < mViewRot.y) mViewRot.y = PIOVER2-0.1f;
	if (mViewRot.x < 0.0f) mViewRot.x += PI2;
	if (mViewRot.x >= PI2) mViewRot.x -= PI2;

	// create the location matrix
	D3DXVECTOR3 forward(0.0f, 0.0f, 1.0f);
	D3DXMATRIX rot;
	D3DXMatrixRotationYawPitchRoll(&rot, mViewRot.x, mViewRot.y, 0.0f);
	// camera forward vector
	D3DXVec3TransformCoord(&mViewForward, &forward, &rot);
	D3DXVec3Normalize(&mViewForward, &mViewForward);
	// camera right vector
	D3DXVec3Cross(&mViewRight, &UP, &mViewForward);
	D3DXVec3Normalize(&mViewRight, &mViewRight);
	*/
}

void TerrainApp::updatePosition(float dt)
{
	float speed = 20.0f;
	float stool = 10.0f;
	if (GetAsyncKeyState('W') & 0x8000) {
		//mViewPos += speed * dt * mViewForward;
		GetCamera().walk(speed * dt);
	}
	if (GetAsyncKeyState('S') & 0x8000) {
		//mViewPos -= speed * dt * mViewForward;
		GetCamera().walk(-speed * dt);
	}
	if (GetAsyncKeyState('A') & 0x8000) {
		//mViewPos -= speed * dt * mViewRight;
		GetCamera().strafe(-speed * dt);
	}
	if (GetAsyncKeyState('D') & 0x8000) {
		//mViewPos += speed * dt * mViewRight;
		GetCamera().strafe(speed * dt);
	}
	//mViewPos.y = mTerrain.getHeight(mViewPos.x, mViewPos.z) + stool;
	//mViewPos.y = stool;
	GetCamera().position().y = stool;
}
void TerrainApp::interactAtSea()
{
	if (!mLeftMouseUp)
		return;
	mLeftMouseUp = false;

	D3DXMATRIX WV = mWaves.getWorldMatrix() * GetCamera().view();
	D3DXMATRIX invWV;
	D3DXMatrixInverse(&invWV, 0, &WV);
	
	D3DXVECTOR3 posV(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 dirV(0.0f, 0.0f, 1.0f);
	D3DXVECTOR3 posO, dirO;
	D3DXVec3TransformCoord(&posO, &posV, &invWV);
	D3DXVec3TransformNormal(&dirO, &dirV, &invWV);
	D3DXVECTOR3 normal(0.0f, 1.0f, 0.0f);
	float t = - D3DXVec3Dot(&normal, &posO) / D3DXVec3Dot(&normal, &dirO);
	D3DXVECTOR3 interO = posO + t * dirO;

	mWaves.pick(interO.x, interO.z);
}

void TerrainApp::buildLights()
{
	float mTheta = 0.75f * PI;
	float mPhi   = 0.25f * PI;
	mParallelLight.dir		= D3DXVECTOR3(
		sinf(mTheta) * cosf(mPhi), cosf(mTheta), sinf(mTheta) * sinf(mPhi) );
	//mParallelLight.ambient	= D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f);
	//mParallelLight.diffuse	= D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	mParallelLight.ambient	= D3DXCOLOR(0.05f, 0.05f, 0.05f, 1.0f);
	mParallelLight.diffuse	= D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f);
	mParallelLight.specular = D3DXCOLOR(2.0f, 2.0f, 2.0f, 1.0f);
}

void TerrainApp::initResources()
{
	mDefaultSpecMapRV	= GetTextureMgr().createTex( L"defaultspec.dds" );
	mEnvMapRV			= GetTextureMgr().createTex( L"uffizi_cross.dds" );

	mTech				 = fx::WaveFX->GetTechniqueByName("WaveTech");
	mfxWVPVar			 = fx::WaveFX->GetVariableByName("gWVP")->AsMatrix();
	mfxWorldVar			 = fx::WaveFX->GetVariableByName("gWorld")->AsMatrix();
	mfxTexMtxVar		 = fx::WaveFX->GetVariableByName("gTexMtx")->AsMatrix();
	mfxReflectMtrlVar    = fx::WaveFX->GetVariableByName("gReflectMtrl")->AsVector();
	mfxCubeMapEnabledVar = fx::WaveFX->GetVariableByName("gCubeMapEnabled")->AsScalar();
	mfxEyePosVar		 = fx::WaveFX->GetVariableByName("gEyePosW");
	mfxLightVar			 = fx::WaveFX->GetVariableByName("gLight");
	mfxDiffuseMapVar	 = fx::WaveFX->GetVariableByName("gDiffuseMap")->AsShaderResource();
	mfxSpecMapVar		 = fx::WaveFX->GetVariableByName("gSpecMap")->AsShaderResource();
	mfxCubeMapVar        = fx::WaveFX->GetVariableByName("gCubeMap")->AsShaderResource();
}



int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
				   PSTR cmdLine, int showCmd)
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif


	TerrainApp theApp(hInstance);
	
	theApp.initApp();

	return theApp.run();
}
